package cn.ibizlab.util.adapter.service.impl;

import cn.ibizlab.util.adapter.service.SysRuntimeAdapterService;
import cn.ibizlab.util.adapter.service.SysRuntimeModelService;
import cn.ibizlab.util.adapter.security.DefaultUserContextRuntime;
import cn.ibizlab.util.adapter.util.constant.DELogicConstant;
import cn.ibizlab.util.adapter.util.helper.CachedBeanCopier;
import cn.ibizlab.util.enums.CheckKeyStatus;
import cn.ibizlab.util.security.AuthenticationUser;
import net.ibizsys.central.dataentity.IDataEntityRuntime;
import cn.ibizlab.util.command.ExecutionCmd;
import cn.ibizlab.util.command.ExecutionCommandContext;
import cn.ibizlab.util.command.ExecutionResult;
import cn.ibizlab.util.domain.EntityBase;
import cn.ibizlab.util.domain.IEntity;
import cn.ibizlab.util.filter.FilterBase;
import lombok.extern.slf4j.Slf4j;
import net.ibizsys.central.dataentity.logic.DELogicSession;
import net.ibizsys.central.dataentity.logic.IDELogicSession;
import net.ibizsys.central.util.IEntityDTO;
import net.ibizsys.central.util.ISearchContext;
import net.ibizsys.model.dataentity.action.IPSDEAction;
import net.ibizsys.model.dataentity.defield.IPSDEField;
import net.ibizsys.model.dataentity.ds.IPSDEDataQuery;
import net.ibizsys.model.dataentity.ds.IPSDEDataSet;
import net.ibizsys.runtime.ISystemRuntime;
import net.ibizsys.runtime.security.IUserContext;
import net.ibizsys.runtime.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.util.*;

/**
 * adapter 适配器服务对象：用于驱动 runtime 执行实体逻辑、前/后附加逻辑及runtime回调标准实体行为、sql调用等处理
 */
@Slf4j
@Service
public class DefaultSysRuntimeAdapterService implements SysRuntimeAdapterService {

    @Autowired
    SysRuntimeModelService sysRuntimeModelService;

    /**
     * 执行实体行为
     * @param iDataEntityRuntime
     * @param iPSDEAction
     * @param args
     * @param actionData
     * @return
     * @throws Throwable
     */
    @Override
    public Object executeAction(IDataEntityRuntime iDataEntityRuntime, IPSDEAction iPSDEAction, Object[] args, Object actionData) throws Throwable {

        if (!(iDataEntityRuntime instanceof DefaultAdapterDataEntityRuntimeBase))
            return null;

        DefaultAdapterDataEntityRuntimeBase dataEntityRuntime = (DefaultAdapterDataEntityRuntimeBase) iDataEntityRuntime;

        Object result;
        String strActionName = iPSDEAction.getName();
        String strName = String.format("%1$s-%2$s",dataEntityRuntime.getName(),strActionName);

        //开启会话
        boolean bOpenActionSession = (ActionSessionManager.getCurrentSession() == null);
        if (bOpenActionSession) {
            ActionSessionManager.openSession().setName(strName);
            //设置用户上下文
            ActionSessionManager.getCurrentSession().setUserContext(createUserContext());
        }
        try {

            String strLastSessionId = ActionSessionManager.getCurrentSession().getSessionId();
            ActionSessionManager.getCurrentSession().setSessionId(KeyValueUtils.genGuidEx());
            ActionSessionManager.getCurrentSession().beginLog(strName, strActionName);

            //runtime 执行实体行为
            result = dataEntityRuntime.onExecuteAction(strActionName ,iPSDEAction ,args ,actionData);

            //runtime在执行行为逻辑时，会修改args中属性值，需将arg中的参数copy至result中
            if(result != null && result instanceof IEntity && !ObjectUtils.isEmpty(args) && args[0] instanceof IEntityDTO){
                cn.ibizlab.util.adapter.util.helper.CachedBeanCopier.copyEntityDto2Entity((IEntityDTO) args[0], (cn.ibizlab.util.domain.IEntity) result);
            }

            // 恢复会话的动态实例运行时
            ActionSessionManager.getCurrentSession().setSessionId(strLastSessionId);
            IActionSessionLog iActionSessionLog = ActionSessionManager.getCurrentSession().endLog(null);

            //行为执行日志
            if (bOpenActionSession) {
                if (iActionSessionLog != null) {
                    if (iActionSessionLog.getTime() >= ActionSessionManager.getLogPOTime()) {
                        sysRuntimeModelService.getSystemRuntime().logPO(ISystemRuntime.LOGLEVEL_WARN, LogCats.PO_DEACTION, iActionSessionLog.toString(true), strName, strActionName, iActionSessionLog.getTime(), iActionSessionLog);
                    }
                }
                ActionSessionManager.closeSession(true);
            }

        } catch (Throwable ex) {
            if (bOpenActionSession) {
                IActionSessionLog iActionSessionLog = ActionSessionManager.getCurrentSession().endLog(ex.getMessage(), true, ex);
                if (iActionSessionLog != null) {
                    String strInfo = String.format("实体[%1$s]行为[%2$s]发生异常，%3$s\r\n%4$s", strName, strActionName, ex.getMessage(), iActionSessionLog.toObjectNode().toString());
                    sysRuntimeModelService.getSystemRuntime().log(LogLevels.ERROR, LogCats.DEACTION, strInfo, ex);
                }

                ActionSessionManager.closeSession(false);
            }
            throw ex;
        }
        return result;
    }

    /**
     * 执行业务端实体行为
     *
     * @param dataEntityRuntime
     * @param iPSDEAction
     * @param arg
     * @return
     * @throws Throwable
     */
    @Override
    public Object executeActionReal(IDataEntityRuntime dataEntityRuntime, IPSDEAction iPSDEAction, Object arg) throws Throwable {

        //入参为dto类型
        if (arg instanceof IEntityDTO) {
            return executeActionRealByEntityDto(dataEntityRuntime, iPSDEAction, (IEntityDTO) arg);
        }

        //入参为其他类型，如：get(key)、remove(key)
        ExecutionCommandContext<IEntity> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(iPSDEAction.getCodeName())
                .arg(arg);

        ExecutionResult result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", iPSDEAction.getName(), result.getMessage()));
        }

        //返回参数转换， get(key) = entity , entity -> entityDto
        Object data = result.getBody();
        if (data != null && data instanceof IEntity) {
            IEntityDTO entityDto = dataEntityRuntime.createEntity();
            cn.ibizlab.util.adapter.util.helper.CachedBeanCopier.copyEntity2Dto((IEntity) data, entityDto);
            data = entityDto;
        }
        return data;
    }

    /**
     * 执行业务端实体行为
     *
     * @param dataEntityRuntime
     * @param iPSDEAction
     * @param entityDto
     * @return
     * @throws Throwable
     */
    protected IEntityDTO executeActionRealByEntityDto(IDataEntityRuntime dataEntityRuntime, IPSDEAction iPSDEAction, IEntityDTO entityDto) throws Throwable {

        //entityDto -> entity
        IEntity entity = entityDto.get(DELogicConstant.PARAM_DATAENTITY) != null ? (IEntity) entityDto.get(DELogicConstant.PARAM_DATAENTITY) : new EntityBase();
        cn.ibizlab.util.adapter.util.helper.CachedBeanCopier.copyEntityDto2Entity(entityDto, entity);

        //执行业务端实体行为
        ExecutionCommandContext<IEntity> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(iPSDEAction.getCodeName())
                .arg(entity);

        ExecutionResult<IEntity> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", iPSDEAction.getName(), result.getMessage()));
        }

        // entity -> entityDto
        cn.ibizlab.util.adapter.util.helper.CachedBeanCopier.copyEntity2Dto(entity, entityDto);

        return entityDto;
    }

    /**
     * 在业务端中执行sql语句
     *
     * @param strSQL
     * @param params
     * @return
     */
    @Override
    public int executeSQLReal(String strSQL, Map<String, Object> params) throws Throwable {

        IDataEntityRuntime dataEntityRuntime = getDataEntityRuntimeFromSession();
        if (dataEntityRuntime == null)
            throw new RuntimeException(String.format("执行sql[%1$s]发生异常，未能获取实体运行时对象", strSQL));

        //执行业务端实体行为
        ExecutionCommandContext<IEntity> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(DELogicConstant.EXECUTE_ACTION_CALLSQL)
                .arg(strSQL)
                .arg(params);

        ExecutionResult<IEntity> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行sql[%1$s]发生异常，异常原因：%2$s", strSQL, result.getMessage()));
        }

        return result.isSuccess() ? 1 : 0;
    }

    /**
     * 执行标准数据查询
     *
     * @param dataEntityRuntime 实体运行时对象
     * @param dataSet           数据集合
     * @param filterDto         查询上下文条件
     * @return
     * @throws Throwable
     */
    @Override
    public Page fetchDataSetReal(IDataEntityRuntime dataEntityRuntime, IPSDEDataSet dataSet, ISearchContext filterDto) throws Throwable {
        //filterDto -> filter
        FilterBase filter = new FilterBase();
        cn.ibizlab.util.adapter.util.helper.CachedBeanCopier.copyFilterDto2Entity(filterDto, filter);

        //执行业务端数据查询
        ExecutionCommandContext<FilterBase> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(String.format("%1$s%2$s", DELogicConstant.EXECUTE_DATASET_PREFIX, dataSet.getCodeName()))
                .arg(filter);

        ExecutionResult<Page<IEntity>> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", dataSet.getName(), result.getMessage()));
        }

        //entity -> entityDto
        Page<IEntity> data = result.getBody();
        List<IEntityDTO> entityDTOS = new ArrayList<>();
        if (!ObjectUtils.isEmpty(data.getContent())) {
            cn.ibizlab.util.adapter.util.helper.CachedBeanCopier.copyEntity2Dto(data.getContent(), entityDTOS);
        }
        return new PageImpl(entityDTOS, data.getPageable(), data.getTotalPages());
    }

    /**
     * 执行标准数据查询
     *
     * @param dataEntityRuntime 实体运行时对象
     * @param query             数据查询
     * @param filterDto         查询上下文条件
     * @return
     * @throws Throwable
     */
    @Override
    public List fetchDataSetReal(IDataEntityRuntime dataEntityRuntime, IPSDEDataQuery query, ISearchContext filterDto) throws Throwable {
        //filterDto -> filter
        FilterBase filter = new FilterBase();
        cn.ibizlab.util.adapter.util.helper.CachedBeanCopier.copyFilterDto2Entity(filterDto, filter);

        String actionName = String.format("%1$s%2$s", DELogicConstant.EXECUTE_DATAQUERY_PREFIX, query.getCodeName());

        //执行业务端数据查询
        ExecutionCommandContext<FilterBase> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(actionName)
                .arg(filter);

        ExecutionResult<List<IEntity>> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", actionName, result.getMessage()));
        }

        //entity -> entityDto
        List<IEntity> data = result.getBody();
        List<IEntityDTO> entityDTOList = new ArrayList<>();
        if (!ObjectUtils.isEmpty(data)) {
            CachedBeanCopier.copyEntity2Dto(data, entityDTOList);
        }
        return entityDTOList;
    }

    /**
     * 检查主键是否存在
     *
     * @param dataEntityRuntime
     * @param key
     * @return
     */
    @Override
    public int checkKeyReal(IDataEntityRuntime dataEntityRuntime, Object key) throws Throwable {

        IPSDEField keyField = dataEntityRuntime.getKeyPSDEField();
        if (keyField == null)
            throw new RuntimeException(String.format("未找到实体[%1$s]主键属性", dataEntityRuntime.getName()));

        //设置实体主键属性值
        IEntity entity = new EntityBase();
        entity.set(keyField.getCodeName(), key);

        //执行业务端检查主键行为
        ExecutionCommandContext<FilterBase> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(DELogicConstant.EXECUTE_ACTION_CHECKKEY)
                .arg(entity);

        ExecutionResult<CheckKeyStatus> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", DELogicConstant.EXECUTE_ACTION_CHECKKEY, result.getMessage()));
        }
        return result.getBody().getValue();
    }

    /**
     * 执行标准插入操作（批处理）
     * @param dataEntityRuntime
     * @param args
     * @return
     */
    @Override
    public Object executeInsert(IDataEntityRuntime dataEntityRuntime, List<Map<String, Object>> args) throws Throwable {

        //执行业务端行为
        ExecutionCommandContext<FilterBase> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(DELogicConstant.EXECUTE_ACTION_CREATE)
                .arg(getEntity(args));

        ExecutionResult<Integer> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", DELogicConstant.EXECUTE_ACTION_CREATE, result.getMessage()));
        }
        return result.getBody();

    }

    /**
     * 执行标准更新操作（批处理）
     * @param dataEntityRuntime
     * @param args
     * @return
     */
    @Override
    public Object executeUpdate(IDataEntityRuntime dataEntityRuntime, List<Map<String, Object>> args) throws Throwable {

        //执行业务端行为
        ExecutionCommandContext<FilterBase> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(DELogicConstant.EXECUTE_ACTION_UPDATE)
                .arg(getEntity(args));

        ExecutionResult<Integer> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", DELogicConstant.EXECUTE_ACTION_UPDATE, result.getMessage()));
        }
        return result.getBody();
    }

    /**
     * 执行标准插入操作（单数据）
     * @param dataEntityRuntime
     * @param arg
     * @return
     */
    @Override
    public Object executeInsert(IDataEntityRuntime dataEntityRuntime, Map<String, Object> arg) throws Throwable {

        //执行业务端行为
        ExecutionCommandContext<FilterBase> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(DELogicConstant.EXECUTE_ACTION_CREATE)
                .arg(getEntity(arg));

        ExecutionResult<Integer> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", DELogicConstant.EXECUTE_ACTION_CREATE, result.getMessage()));
        }
        return result.getBody();
    }

    /**
     * 执行标准更新操作（单数据）
     * @param dataEntityRuntime
     * @param arg
     * @return
     */
    @Override
    public Object executeUpdate(IDataEntityRuntime dataEntityRuntime, Map<String, Object> arg) throws Throwable {

        //执行业务端行为
        ExecutionCommandContext<FilterBase> context = new ExecutionCommandContext()
                .setEntity(dataEntityRuntime.getPSDataEntity().getCodeName())
                .setAction(DELogicConstant.EXECUTE_ACTION_UPDATE)
                .arg(getEntity(arg));

        ExecutionResult<Integer> result = ExecutionCmd.execute(context);
        if (!result.isSuccess()) {
            throw new RuntimeException(String.format("执行实体行为[%1$s]发生异常，异常原因：%2$s", DELogicConstant.EXECUTE_ACTION_UPDATE, result.getMessage()));
        }
        return result.getBody();
    }

    /**
     * 参数转换 map -> entity
     *
     * @param args
     * @return
     */
    protected List<IEntity> getEntity(List<Map<String, Object>> args) {
        List<IEntity> list = new ArrayList<>();
        if (!ObjectUtils.isEmpty(args))
            args.forEach(item -> list.add(getEntity(item)));

        return list;
    }

    /**
     * 参数转换 map -> entity
     *
     * @param arg
     * @return
     */
    protected IEntity getEntity(Map<String, Object> arg) {
        IEntity entity = new EntityBase();
        if (!ObjectUtils.isEmpty(arg)) {
            for (Map.Entry<String, Object> entry : arg.entrySet()) {
                entity.set(entry.getKey(), entry.getValue());
            }
        }
        return entity;
    }

    /**
     * 从session中获取实体运行时对象
     *
     * @return
     */
    protected IDataEntityRuntime getDataEntityRuntimeFromSession() {
        IDataEntityRuntime dataEntityRuntime = null;
        IDELogicSession logicSession = DELogicSession.getCurrent();
        if (logicSession != null && logicSession.getDELogicRuntime() != null) {
            dataEntityRuntime = logicSession.getDELogicRuntime().getDataEntityRuntime();
        }
        return dataEntityRuntime;
    }

    /**
     * 创建用户上下文
     *
     * @return
     */
    protected IUserContext createUserContext() {
        AuthenticationUser user = AuthenticationUser.getAuthenticationUser();
        DefaultUserContextRuntime context = new DefaultUserContextRuntime();
        context.setOrgid(user.getOrganizationId());
        context.setOrgname(user.getOrganizationName());
        context.setOrgcode(user.getOrganizationNumber());
        context.setDeptid(user.getDepartmentId());
        context.setDeptname(user.getDepartmentName());
        context.setDeptcode(user.getDepartmentNumber());
        context.setUserid(user.getUserid());
        context.setUsername(user.getUsername());
        return context;
    }

}
